--- title: "14-C进阶 图形化 easyX" created: 2025-11-25 tags: - 项目筑基 --- # C进阶 图形化 easyX ### 概念 #### easyX的颜色 ![[easyx-image1.png]] easyX中的颜色实际上都是用三原色 红绿蓝 表示出来的 ```cpp 函数RGB(红色部分,绿色部分,蓝色部分); ``` 每个颜色的值都是从0~255 Tips: 使用qq截图 ctrl+alt+a 按c选取颜色 可以直接复制进入RGB函数 ![[easyx-image2.png]] #### easyX的坐标 坐标原点默认为窗口的左上角,X轴向右为正,Y轴向下为正,度量单位为像素点 ![[easyx-image3.png]] 使用的是逻辑坐标 它可以进行修改 ```cpp setorigin(x,y); setaspectratio(1, -1); ``` 将x,y定义为逻辑坐标原点 缩放坐标系 为负时即翻转坐标轴(翻转后字符也会翻) ![[easyx-image4.png]] ![[easyx-image5.png]] ### 关于窗口 #### 创建 初始化绘图窗口(创建窗口) ![[easyx-image6.png]] ```cpp initgraph(800, 600); ``` 创建一个宽800 高600的窗口 注意!这个不是控制台 是图形化界面 如果要显示控制台的话 可以使用 ```cpp initgraph(800, 600, EX_SHOWCONSOLE); ``` 恶搞使用 ```cpp initgraph(800, 600, EX_NOCLOSE);//禁用绘图窗口的关闭按钮 initgraph(800, 600, EX_NOMINIMIZE);//禁用绘图窗口的最小化按钮 ``` 当然可以加在一起: ```cpp initgraph(800, 600, EX_SHOWCONSOLE | EX_NOCLOSE | EX_NOMINIMIZE); ``` ![[easyx-image7.png]] #### 关闭 ```cpp 有创建 对应就要有关闭窗口 closegraph(); ``` ![[easyx-image8.png]] 避免一闪而过 使用getchar();函数进行一个缓冲 回车后才会执行close指令 #### 背景 大黑框太限制发挥了 这个窗口可以自己设置背景色 ```cpp setbkcolor(WHITE);//设置背景色 cleardevice();//搭配使用 否则仍为黑 ``` 先setbkcolor再cleardevice 组合才能实现换色 ```cpp 一切关于颜色的都可以使用GRB函数 setbkcolor(GRB( , , ,)); ``` #### 模板 在进行操作前先打一套组合拳 ```cpp #include #include int main() { initgraph(800, 600); setorigin(400, 300); //setaspectratio(1, -1);(不建议翻转 因为后续可能需要文字输出) setbkcolor(WHITE); cleardevice(); …… getchar(); closegraph(); return 0; } ``` ### 基本绘制图形 ![[easyx-image9.png]] 点、线、面—— 圆、矩形、椭圆、圆角矩形、扇形、弧形(arc) 多边形 #### 点(putpixel) ![[easyx-image10.png]] x,y为坐标位置 第三个参数为颜色 绘制2个点 ```cpp putpixel(0, 0, RED); putpixel(200, 200, BLUE); ``` 随机绘制1000个点(x - 400~400 y - 300~300) ```cpp int x, y; for (int i = 0; i < 1000; i++) { x = rand() % (800 + 1) - 400; y = rand() % (600 + 1) - 300; putpixel(x, y, RED); } ``` ![[easyx-image11.png]] #### 线(line) ![[easyx-image12.png]] ```cpp line(-300, 0, 300, 0); ``` 两点确定一条线 所以给出两个坐标即可 ```cpp setlinecolor(RED);//默认为白色 会与背景融合 所以设置一下颜色 line(-300, 0, 300, 0); ``` ![[easyx-image13.png]] #### 圆(circle) ![[easyx-image14.png]] ![[easyx-image15.png]] ```cpp setlinecolor(RGB(190, 183, 255)); 同心圆 circle(0, 0, 100);//圆心(x,y),半径 ``` ![[easyx-image16.png]] ![[easyx-image17.png]] #### 矩形(rectangle) ![[easyx-image18.png]] 与截屏同理 从左上角选至右下角即可确定一个矩形 两点就行 不一定要左上到右下 ```cpp 所以rectangle(-200, -100, 200, 100); ``` ![[easyx-image19.png]] #### 椭圆(ellipse) ![[easyx-image20.png]] 椭圆跟矩形联系密切 是它的内切 所以也是给出两点 ```cpp rectangle(-200, 100, 200, -100); ellipse(-200, 100, 200, -100); ellipse(-200, 100, 200, -100); ``` ![[easyx-image21.png]] ![[easyx-image22.png]] #### 圆角矩形(roundrect) ![[easyx-image23.png]] ![[easyx-image24.png]] ![[easyx-image25.png]] 四个角变成了椭圆的弧 前面4个参数仍然是两个点的坐标 后面两个参数则为构造圆角的椭圆的宽与高(简单点就是[x1-x2/2]和[y1-y2]/2) 但是最后两个参数有很多讲究 用好了可以变成像圆但有一部分平的图形 用来当脑袋什么的 #### 扇形(pie) ![[easyx-image26.png]] ![[easyx-image27.png]] ![[easyx-image28.png]] 前四个参数为两点坐标 参数5:起始角弧度 参数6:从起始 顺时针 旋转的一个度数 需要宏定义好 ```cpp #define PI 3.14 ``` #### 弧形(arc) ![[easyx-image29.png]] ![[easyx-image30.png]] 与pie基本相同 只是arc不会绘制从圆心到圆周的线段 ```cpp pie(-200, 100, 200, -100, 0, PI / 4); arc(-200, 100, 200, -100, PI/4, PI /2 ); ``` ![[easyx-image31.png]] #### 多边形(polygon) ![[easyx-image32.png]] 用一个结构体存储想构造的图形的几个顶点 传入这个结构体的首地址(指针) 和 边数 比如: 三角形 ```cpp POINT points[] = { {0,200},{200,-200},{-200,-200} }; polygon(points, 3); ``` ![[easyx-image33.png]] 四边形 ```cpp POINT points2[] = { {200,200},{200,-200},{-200,-200},{-200,200 } }; polygon(points2, 4); ``` ![[easyx-image34.png]] 梯形 ```cpp POINT points3[] = { {100,200},{200,-200},{-200,-200},{-100,200 }}; polygon(points3, 4); ``` ![[easyx-image35.png]] 五边形 ```cpp double theta = PI / 2; ``` ![[easyx-image36.png]] ```cpp double delta = 2 * PI / 5; int r = 200; POINT point4[5]; for (int i = 0; i < 5; i++) { point4[i].x = cos(theta + i * delta) * r; point4[i].y = sin(theta + i * delta) * r; } polygon(point4, 5); ``` 初始角度为90度 循环一次加72度 利用循环得出每个点的x,y ![[easyx-image37.png]] ![[easyx-image38.png]] Polygon是将每个点依次连接 然后连接首位 (p1~p2~…~p8依次连 然后p8连p1) 如果想描绘非封闭图形 比如字母M ![[easyx-image39.png]] 就需要 #### 不封闭图形(polyline) ```cpp POINT point5[5] = { {-400,200},{-200,-200},{0,100},{200,-200},{400,200} }; polyline(point5, 5); ``` ![[easyx-image40.png]] ### 图形设计 非封闭图形(点线弧…)可以设计它的边框 封闭图形可以设计它的边框以及填充 ![[easyx-image41.png]] 在circle、rectangle……前面加上fill或者solid #### 边框颜色 ![[easyx-image42.png]] ```cpp setlinecolor(RED); setlinecolor(RGB(214, 157, 133)); ``` 后面的覆盖前面的 由此可以衍生很多颜色的图形 #### 边框样式 一般用这俩参数 ![[easyx-image43.png]] ![[easyx-image44.png]] ```cpp setlinestyle(PS_DOT); setlinestyle(PS_DOT,5); ``` ![[easyx-image45.png]] ![[easyx-image46.png]] 关于自定义边框 详见https://docs.easyx.cn/zh-cn/setlinestyle ![[easyx-image47.png]] #### 填充颜色 ![[easyx-image48.png]] ```cpp setfillcolor(YELLOW); ``` 同样能用RGB函数 #### 填充样式 ![[easyx-image49.png]] ![[easyx-image50.png]] ```cpp setlinecolor(RGB(214, 157, 133)); setlinestyle(PS_DOT); setfillcolor(RED); setfillstyle(BS_HATCHED, HS_FDIAGONAL); POINT points2[] = { {200,200},{200,-200},{-200,-200},{-200,200 } }; fillpolygon(points2, 4); ``` ![[easyx-image51.png]] 自定义填充? ### 文本输入 #### 绘制文字 ```cpp outtextxy(int x,int y,文字); ``` 在(x,y)输出文字str ```cpp outtextxy(int x,int y,LPCTSTR str); 如outtextxy(10,10,"你好"); ``` 注意:使用字符串时出现错误 解决方法: 在字符串前加上L ```cpp 即outtextxy(10,10,L"你好"); ``` 2.进入项目 -> 属性 ->配置属性 ->常规 -> 字符集 -> 改为多字节字符集 ![[easyx-image52.png]] ![[easyx-image53.png]] #### 字体颜色 ```cpp settextcolor(颜色); 如settextcolor(BLUE); settextcolor(RGB(150, 187, 202)); ``` #### 字体样式 ```cpp settextstyle(int Height,int Width, 样式); ``` 大小(指定高度、宽度[0为自适应])(也可以理解为文本框的左上角的地址) 字体 ```cpp 如settextstyle(30,0,L"行楷"); ``` #### 文字背景 TRANSPARENT透明 OPAQUE,BKMODE_LAST不透明 ```cpp setbkmode(TRANSPARENT); settextcolor(RED); settextcolor(RED); settextstyle(50, 0, L"宋体"); settextstyle(50, 0, L"宋体"); outtextxy(-130, -30, L"hello easyx"); setbkmode(TRANSPARENT); outtextxy(-130, -30, L"hello easyx"); ``` ![[easyx-image54.png]] ![[easyx-image55.png]] #### 文本居中于图像 如果一个像素一个像素地调 太麻烦 而且字符一变就不居中了 所以得想办法 需要用到两个函数 ```cpp 获取文本的宽度textwidth(TCHAR c); 获取文本的高度textheight(TCHAR c); ``` (给出的不是字符串长度 而是像素宽度) ```cpp VS 2022需要传入一个TCHAR类型的数组 VS 6.0可以直接传入文本 如textwidth("哈哈哈哈"); ``` 给出想看到的文本 使用TCHAR类型的数组 而后传入到text…函数中获取宽、高 ```cpp TCHAR arr[] = L"居中"; ``` 求出字符串长度/2 框的一半长度减字符串的一半长度 就是字符离框的距离 ```cpp int width = (400-200)/2-textwidth(arr)/2; int height =(150-50)/2- textheight(arr) / 2; ``` 框的一半长度(y) - 字符串(文本框)的一半长度(x) = n(x轴方向 文本框离图形框的距离) m(图形框的横坐标)+ n(刚才求出来的) = 文本框的左上角的横坐标 同理 还能求出文本框左上角的纵坐标 知道这个点在哪里 就能实现文字居中于图像 ```cpp //然后再把框的左上角的x加上这个width y+height 就是字符的居中位置 outtextxy(width + 200, height + 50, arr); ``` 实现: ![[easyx-image70.png]] ```cpp setlinecolor(RGB(214, 157, 133)); setlinestyle(PS_DOT); setfillcolor(RED); setfillstyle(BS_HATCHED, HS_FDIAGONAL); fillellipse(-200, -100, 200, 100); settextcolor(RED); settextstyle(50, 0, L"宋体"); setbkmode(TRANSPARENT); TCHAR arr[] = L"hello easyX"; int width = 400 / 2 - textwidth(arr) / 2; int height = 200 / 2 - textheight(arr) / 2; outtextxy(width - 200, height - 100, arr); ``` ##### 封装一下 ```cpp void juzhong(int x, int y, int w, int h) { setbkmode(TRANSPARENT); setfillcolor(BROWN); //可以随意更换图形 给出左上角的坐标 给出长和宽 就有两个点坐标 fillellipse (x, y, x + w, y + h); //fillroundrect(x, y, x + w, y + h, 10, 10); settextstyle(30, 0, L"黑体"); TCHAR text[] = L"文本"; int tx = x + ( w - textwidth(text)) / 2; int ty = y + ( h - textheight(text))/ 2; outtextxy( tx , ty, text); } ``` 这样一来 任给一个图形的左上角坐标和期望的大小 就能将文字居中在图像内部 因为要从主函数传入想输入的文本 所以得多一个参数 只能用TCHAR类型就很烦 ```cpp void button(int x, int y, int w, int h, TCHAR* text) { setbkmode(TRANSPARENT); setfillcolor(BROWN); fillroundrect(x, y, x + w, y + h, 10, 10); settextstyle(30, 0, L"黑体"); TCHAR text_[100]; for (int i = 0; i < 100; i++) text_[i] = text[i]; int tx = x + ( w - textwidth(text)) / 2; int ty = y + ( h - textheight(text))/ 2; outtextxy( tx , ty, text); } ``` 主函数中还要另外这样 ```cpp TCHAR a[] = L"文本"; button(50, 50, 150, 50,a); ``` 只要给出携带文本的图形大小就OK了(填充、样式在函数里改) ### 图片输出 定义一个IMAGE类型的变量对象 ```cpp IMAGE image1; ``` #### 加载图片loadimage ![[easyx-image71.png]] 最少传入两个参数 ——前面定义的IMAGE类型变量的地址和图片文件地址 有两种方式填入图片文件地址 相对路径 需要把图片保存在CPP文件的同一目录下 ./表示当前文件夹下 ../表示当前文件夹的父目录下 ```cpp loadimage(&image1, _T("./cat.jpg")); ``` 一定要用 _T()去进行一个转换 否则报错 绝对路径 使用绝对路径 就不需要将图片要放CPP同一个目录里 复制到的绝对路径是这样的"D:\EasyX代码\图像输出1\图像输出1\cat.jpg" 需要对它进行一点修改 多加一个转义字符/将各目录区分开 ```cpp loadimage(&image1, _T("D:\\EasyX代码\\图像输出1\\图像输出1\\cat.jpg")); ``` 或者把\改成/ 名字有空格的文件夹还要加上双引号 然后前面再加一个\ 麻烦 但是有点是无需在同一目录 ```cpp loadimage(&image1, _T("D:\\easyX素材\\82c0c33265135abb203bb51d9cae4302.jpeg")); ``` 不知道为什么 反正地址的传入都需要使用_T()去进行转换 ![[easyx-image72.png]] 在这个函数中还可以加入两个参数 ```cpp loadimage(&image1, _T("./cat.jpg"), 500, 500); ``` 去定义该图片的宽、高方向上的像素 (这是加载时的步骤 不是输出时的步骤) #### 输出图片putimage ![[easyx-image73.png]] 三个参数 想把图片输出在哪里?给出它左上角的坐标xy 然后把一开始那个指针(已经经过加载 将图片传入)传进去 ```cpp putimage(-400, -300, &image1); ``` ### 鼠标操作 定义好一个按钮 用前面文字居中的函数(封装一下) ![[easyx-image74.png]] 使用两个函数 ExMessage储存鼠标信息 仅需要定义一个该结构的变量就行 它会储存你的鼠标在哪里 进行了什么操作 至于鼠标进行了什么操作该有什么反应的工作交给其他指令 ![[easyx-image75.png]] peekmessage ![[easyx-image76.png]] ```cpp peekmessage(&msg, EX_MOUSE); ``` 将存储了鼠标信息的结构体的地址传入 并告知是需要它判断对鼠标的操作 ![[easyx-image77.png]] 因为它的返回值是 ![[easyx-image75.png]] ![[easyx-image78.png]] 所以与if连用 如果接收到了 怎么样 这是接收与否 那接受的是什么呢 点击?悬挂?右键? 使用switch case去决定哪一种操作要干什么 ```cpp initgraph(640, 480, EX_SHOWCONSOLE); TCHAR a[] = L"按钮"; button(50, 50, 150, 50,a); 给出按钮在界面中的位置 左上角(50,50) 宽150 高50 ExMessage msg; 定义ExMessage类型的变量msg while (true){ 无限循环 if (peekmessage(&msg, EX_MOUSE)){ peekmessage判断是否接收到了鼠标信息 switch (msg.message){ case WM_LBUTTONDOWN: 如果是点击左键 那么输出welcome if (msg.x >= 50 && msg.x <= 50 + 150 && msg.y >= 50 && msg.y <= 50 + 50){ printf("welcome\n"); } 当然是要在50,50 200,100这个范围点击 break; case WM_RBUTTONDOWN: 如果是点击右键 那么输出hello if (msg.x >= 50 && msg.x <= 50 + 150 && msg.y >= 50 && msg.y <= 50 + 50){ printf("hello\n"); } break; } } } ``` 注意! ExMessage给鼠标传的坐标是物理坐标而不是逻辑坐标 ![[easyx-image79.png]] ![[easyx-image80.png]] 如果使用了setorigin定义逻辑坐标点 我们的图形是根据这个逻辑坐标确定位置的 但是 鼠标的位置不由逻辑坐标的改变而改变 而是根据物理坐标定位 所以不能简单的使用图形框的左上角坐标去加上宽高来确定鼠标点击的区域 也可以使用outtext函数实现在图形化界面输出 ![[easyx-image81.png]] 还可以使用输出鼠标当前所在坐标的形式 验证之前 鼠标所使用的是物理坐标的结论 ```cpp ExMessage msg; while (true) { if (peekmessage(&msg, EX_MOUSE)) { printf("坐标(%d,%d)\n", msg.x, msg.y); } } ``` ![[easyx-image82.png]] (去定义一个逻辑坐标点 对比两种情况下的鼠标位置的坐标变化 会发现跟逻辑坐标在哪没关系) 那么是不是可以通过这种方式来去确定想知道的点的坐标 来帮助绘图 这样的话就一定要省去定义逻辑坐标的步骤 不然搞得晕头转向的 ```cpp ExMessage msg; while (true){ if (peekmessage(&msg, EX_MOUSE)){ switch (msg.message){ case WM_RBUTTONDOWN: if (msg.x >= 0 && msg.x <= 1000 && msg.y >= 0 && msg.y <= 650){ printf("坐标(%d,%d)\n", msg.x, msg.y); } ``` ![[easyx-image83.png]] ```cpp break; } } } ``` 逻辑坐标的范围0,0到多少都行 不过最好用自己定义的页面大小 ### 键盘操作 两种方法 ```cpp getch() ``` 需要使用头文件 conio.h ```cpp GetAsyncKeyState(键值); ``` 需要使用头文件windows.h 但EasyX里包含了 ![[easyx-image84.png]] #### _getch(); 需要使用返回值来判断 字符那边使用的是ASCII码比较 如 ```cpp while(1) { char key = _getch(); printf("%d,%c\n",key,key); } ``` 得到的全是a~z的ASCII码值 如果是按上下左右键 ![[easyx-image85.png]] ![[easyx-image86.png]] ![[easyx-image87.png]] 返回的则是虚拟键值 上:72 下:80 左:75 右:77 所以 可以使用switch语句 设置上下左右键→ → 在循环外加上x,y的初始化 创建一个圆 坐标为xy 半径为20 在按下w/W/↑时y-- ```cpp …… ``` (注意 向上y是减减 因为y轴朝下) 得到这段代码 可以控制球的移动 ```cpp int x=0, y = 0; while(1) { setfillcolor(BROWN); ``` ![[easyx-image88.png]] ```cpp fillcircle(x, y, 20); char key = _getch(); printf("%d,%c\n",key,key); switch (key) { case 72: case 'w': case 'W': y--; printf("上键\n"); break; case 80: case 's': case 'S': y++; printf("下键\n"); break; case 75: case 'a': case 'A': x--; printf("左键\n"); break; case 77: case 'd': case 'D': x++; printf("右键\n"); break; } } 觉得移动太慢可以修改每按一次x,y的改变值 比如x+=5; ``` 这样是会留有球的运动轨迹的(因为没有清屏) 用这个特性是不是还可以把球的位置定义成鼠标返回的xy值 然后跟随鼠标移动而绘图 一个简易绘图板就出来了 问题在于不能抬笔 只能一笔画 如果要实现 那就使用如果点击左键就画画 要更像一点画图板的话 就要加上清屏功能 ```cpp ExMessage msg; while (true) ``` ![[easyx-image89.png]] ```cpp { if (peekmessage(&msg, EX_MOUSE)) { setfillcolor(BROWN); fillcircle(msg.x,msg. y, 5); } } ``` ![[easyx-image90.png]] 或者这样 判断鼠标移动信息 ```cpp ExMessage msg; while (true) { if (peekmessage(&msg, EX_MOUSE)) { switch (msg.message) { case WM_MOUSEMOVE: setfillcolor(BROWN); fillcircle(msg.x, msg.y, 5); } } } ``` 想实现长按左键画图 但失败了 ```cpp case WM_LBUTTONDOWN && WM_MOUSEMOVE: setfillcolor(BROWN); fillcircle(msg.x, msg.y, 5); ``` 布尔运算符|不行 逻辑运算符&&也不行 跑偏了 那么怎么实现不留球的运动轨迹 ```cpp cleardevice(); ``` ![[easyx-image91.png]] 本来尝试使用clearcircle函数 (生成的是一个圆 每次都清除上一个生成的圆 但是不知道为什么失败了) ![[easyx-image92.png]] 可能是位置问题 原来……此清除圆非彼清除圆 ![[easyx-image93.png]] ![[easyx-image94.png]] 等等 它删除的是圆内的东西 留下来边框 那如果是无边框填充的图形呢 生成一个圆显示完后就立刻删除 成功实现 所以清除一个圆的轨迹有两种方法 一、清除全屏 ```cpp int x=0, y = 0; while(1) { cleardevice(); setfillcolor(BROWN); solidcircle(x, y, 20); char key = _getch(); printf("%d,%c\n",key,key); switch (key) {…… ``` 清除该圆区域 ```cpp int x=0, y = 0; while(1) { setfillcolor(BROWN); solidcircle(x, y, 20); char key = _getch(); clearcircle(x, y, 20); printf("%d,%c\n",key,key); switch (key) {…… ``` 对于第一种就存在一个问题 如果在while循环外设置的背景也会一起被清除 要避免这种情况 就需要把所有东西放进while循环 第二种不确定圆区域内的背景会不会被清除?如果会那么跟第一种就没啥区别 好吧 会 那么 又衍生出一种玩法:把这个图片“吃掉”! ![[easyx-image95.png]] 老老实实把所有东西都放入循环 清屏下面 ```cpp int x = 0, y = 0; while (1) { cleardevice(); IMAGE image1; loadimage(&image1, _T("D:\\easyX素材\\fb517c4617291f66e1809b007ad9c430.jpeg")); putimage(0, 0, &image1); ``` ![[easyx-image96.png]] ```cpp setfillcolor(BROWN); solidcircle(x, y, 20); char key = _getch(); //clearcircle(x, y, 20); printf("%d,%c\n", key, key); switch (key) {…… ``` 又一个问题出来了 我们会发现在这个过程中 图片会一闪一闪 明显卡顿 如果仅清除一个区域 卡顿的就不是图片 只有球一闪一闪了 ![[easyx-image97.png]] 怎么解决? 三个函数: ```cpp BeginBatchDraw();开始批量绘图 FlushBatchDraw(); EndBatchDraw();结束批量绘制 ``` ![[easyx-image98.png]] 之前防止清屏是将所有步骤都放入循环 在这个循环里不断绘制 不断清屏 循环往复 我们每个过程都看到了 所以会产生卡顿 那么这几个函数 就是在内存里存在一个缓冲区 提前帮我们绘制好 在需要时直接调用出来(所谓批量绘图) 这样我们就能直接看到图片显示 而看不到它绘制的过程 所以就解决了卡顿的问题 ![[easyx-image99.png]] ```cpp #include #include #include int main() { initgraph(800, 600, EX_SHOWCONSOLE); BeginBatchDraw(); int x = 0, y = 0; while (1) { cleardevice(); IMAGE image1; loadimage(&image1, _T("D:\\easyX素材\\fb517c4617291f66e1809b007ad9c430.jpeg")); putimage(0, 0, &image1); setfillcolor(BROWN); solidcircle(x, y, 20); FlushBatchDraw(); char key = _getch(); //clearcircle(x, y, 20); printf("%d,%c\n", key, key); switch (key) { case 72:case 'w':case 'W': y -= 3;printf("上键\n");break; case 80:case 's':case 'S': y += 3;printf("下键\n");break; case 75:case 'a':case 'A': x -= 3;printf("左键\n");break; case 77:case 'd':case 'D': x += 3;printf("右键\n");break; } } EndBatchDraw(); getchar(); closegraph(); return 0; } ``` #### GetAsyncKeyState(键值); ![[easyx-image183.png]] ```cpp initgraph(800, 600, EX_SHOWCONSOLE); BeginBatchDraw(); int x = 0, y = 0; while (1) { cleardevice(); IMAGE image1; loadimage(&image1, _T("D:\\easyX素材\\fb517c4617291f66e1809b007ad9c430.jpeg")); putimage(0, 0, &image1); setfillcolor(BROWN); solidcircle(x, y, 20); FlushBatchDraw(); if (GetAsyncKeyState(VK_UP)) { y -= 1; } if (GetAsyncKeyState(VK_DOWN)) { y += 1; } if (GetAsyncKeyState(VK_LEFT)) { x -= 1; } if (GetAsyncKeyState(VK_RIGHT)) { x += 1; } } EndBatchDraw(); getchar(); closegraph(); ``` 这种方法比较丝滑 灵敏度高 但是只能使用↑↓←→ D:\EasyX代码\键盘操作\键盘操作\源.cpp ### 音乐播放 首先要用到两个头文件 ```cpp #include #include//包含多媒体设备接口头文件 ``` 或者#include (easyx.h里包含了这两个 如果用了easyx就可以不用包含) 还需要加载静态库winmm.lib #pragma comment (lib,"Winmm.lib")//加载静态库 接着使用mciSendString函数进行打开、播放、重复播放、关闭操作 ![[easyx-image184.png]] ![[easyx-image185.png]] ![[easyx-image186.png]] alias取别名 如果使用的Unicode字符集 相对路径: 从本级文件夹打开音乐 ```cpp mciSendString(L"open ./BGM2.mp3 alias a", NULL, 0, NULL); ``` 播放音乐 ```cpp mciSendString(L"play a", NULL, 0, NULL); ``` 重复播放 ```cpp mciSendString(L"repeat a", NULL, 0, NULL); mciSendString(L"play a repeat", NULL, 0, NULL); ``` 关闭音乐 ```cpp mciSendString(L"close a", NULL, 0, NULL);//记得用if条件 不然一打开就关闭了 ``` 绝对路径: ```cpp mciSendString(L"open D:\\easyX素材\\BGM1.mp3 alias a", NULL, 0, NULL); mciSendString(L"play a", NULL, 0, NULL); mciSendString(L"repeat a", NULL, 0, NULL); mciSendString(L"play a repeat", NULL, 0, NULL); mciSendString(L"close a", NULL, 0, NULL); ``` D:\EasyX代码\音乐\音乐\源.cpp 使用多字符集就无需L ![[easyx-image52.png]] ![[easyx-image53.png]] 利用字符处理函数 进行一个封装 可在任何地方加PlayMusic(地址)会结束前一首歌 而播放下一首 可用于场景切换 ```cpp #include #include #include//包含多媒体设备接口头文件 ``` #pragma comment (lib,"Winmm.lib")//加载静态库 ```cpp void PlayMusic(const char* FileName) { char cmdString[50] = "open "; strcat_s(cmdString, FileName); strcat_s(cmdString, " alias bkmusic"); mciSendString("close bkmusic", NULL, 0, NULL);//把上一次的音乐关掉 mciSendString(cmdString, NULL, 0, NULL);//打开音乐 mciSendString("play bkmusic", NULL, 0, NULL);//播放一次音乐 } int main() { PlayMusic("D:\\easyX素材\\BGM1.mp3"); //仅需给出地址 多加一个\ _getch(); return 0; } ``` D:\EasyX代码\音乐2\音乐2\源.cpp 引用的音乐文件名最好改成中文的 而且不能有空格 格式方面:使用qq音乐的音频转码去改成MP3格式 ![[easyx-image187.png]] ![[easyx-image188.png]] ### 修改窗口标题以及弹窗 #### 窗口标题 ```cpp GetHWnd();获取窗口句柄 获取之后用于操作窗口 ``` 定义一个HWND类型的变量存储获取到的句柄 ```cpp HWND hnd = GetHWnd(); ``` 可以对我们的图形化界面去进行操作了 ```cpp SetWindowText(hnd, L"hello easyx"); ``` hnd是我们传入的HWND类型变量 后方是想设置的图形化窗口标题 SetWindowText函数包含在window.h里 但是easyx包含了windows.h 没用图形化界面的话也用不着修改它的名字 所以这个无所谓 图形化窗口可以改 其实控制台的窗口的名字也能改 System(“title 任取名字”) ```cpp system("title 我是控制台"); ``` ![[easyx-image189.png]] 养成习惯封装 ```cpp void change() { system("title 我是控制台");//修改控制台名字 //获取窗口句柄 HWND hnd = GetHWnd(); SetWindowText(hnd, L"hello easyx"); } ``` #### 弹窗 ```cpp MessageBox(1,2,3,4); ``` ![[easyx-image190.png]] 四个参数: 窗口句柄(可缺省) 提示框里面的东西 提示框标题 按钮(必须要有) ```cpp MessageBox(NULL, L"文本内容", L"标题", MB_OKCANCEL); MessageBox(hnd, L"文本内容", L"标题", MB_YESNO); ``` 区别在于 如果写上了窗口句柄 就必须先点击这个弹窗才能进行图形界面的其他操作 ![[easyx-image191.png]] 长这样 按钮是不可缺省的 包括 ```cpp MB_OKMB_OKCANCELMB_ABORTRETRYIGNOREMB_YESNOCANCEL MB_YESNO MB_RETRYCANCEL ``` ![[easyx-image192.jpeg]] MB_OK ![[easyx-image193.png]] MB_OKCANCEL ![[easyx-image194.png]] MB_ABORTRETRYIGNORE ![[easyx-image195.png]] MB_YESNOCANCEL ![[easyx-image196.png]] MB_YESNO ![[easyx-image197.png]] MB_RETRYCANCEL ![[easyx-image198.png]] 但是按了不同按钮应该会有后续不同操作吧 所以就需要if去判断 怎么判断? ![[easyx-image199.png]] ![[easyx-image200.png]] 打开MessageBox函数的定义 发现它的返回值是一个int 那么就可以定义一个int类型去存储它 ```cpp int n = MessageBox(hnd, L"文本内容", L"标题", MB_OKCANCEL); ``` 那跟什么判断呢?因为不知道返回值是什么 直接去与宏比较 ```cpp if (n == IDYES)//不知道返回了什么 那就直接和宏比 { printf("你点击了确定\n"); } if (n == IDNO) { printf("你点击了取消\n"); } ``` 比较对象是和MessageBox里第四个参数有关的 如果用了MB_OKCANCEL 就与IDOK 和ODCANCEL比较 ![[easyx-image201.png]] ```cpp #include #include void change() { system("title 我是控制台"); HWND hnd = GetHWnd(); SetWindowText(hnd, L"hello easyx");/ int n = MessageBox(hnd, L"文本内容", L"标题", MB_OKCANCEL); if (n == IDYES){ printf("你点击了确定\n"); } if (n == IDNO) { printf("你点击了取消\n"); } } int main() { initgraph(800, 600,EX_SHOWCONSOLE); change(); getchar(); closegraph(); return 0; } ``` D:\EasyX代码\修改窗口标题\修改窗口标题\源.cpp ## 贪吃蛇 构建好框架 留下死循环以便绘图 ```cpp #include #include int main() { initgraph(640, 480); setbkcolor(RGB(206, 212, 240)); cleardevice(); while (true) { } getchar(); closegraph(); return 0; } ``` 想定义蛇 应该有一个结构体类型存储它 要有坐标coor 蛇的节数size 移动方向dir 移动速度speed 以及分数 ```cpp #define SNAKE_NUM 500 struct Snake { int size; int dir; int speed; POINT coor[SNAKE_NUM]; }; ``` ![[easyx-image202.png]] Point是一个已经定义好了的结构体 内含x,y 因为蛇的每一节都应该有一个坐标 所以得用结构体数组去存储每一节的坐标 又因为不知道蛇到底有多长 所以先用宏定义 把长度设置为500 不够的话改一下宏定义就好了 数据初始化 让蛇一开始就有三节 数组0的方向为蛇头 后面加蛇身更方便 ```cpp void GameInit() { snake.size = 3; ``` ![[easyx-image203.png]] ```cpp snake.speed = 10; snake.dir; //先不管 for (int i = snake.size - 1; i >= 0; i--) { snake.coor[i].x = 40-10 * i; snake.coor[i].y = 10; } } ``` 再采用封装的方式 把主函数中的窗口绘制也放入函数当中去 后面考虑到方向 所以这里把初始化方向设置为向右 ```cpp void GameInit() { initgraph(640, 480); snake.size = 3; snake.speed = 10; snake.dir=RIGHT; for (int i = snake.size - 1; i >= 0; i--) { snake.coor[i].x = 40-10 * i; snake.coor[i].y = 10; } } void GameDraw() { //设置背景颜色 setbkcolor(RGB(206, 212, 240)); cleardevice(); //绘制蛇 三节、每节多长 有坐标 再加上半径就行了 for (int i = 0; i < snake.size; i++) { solidcircle(snake.coor[i].x, snake.coor[i].y, 5); } } ``` ![[easyx-image204.png]] 蛇的移动 防止卡顿就需要用到双缓冲绘图 ```cpp BeginBatchDraw(); FlushBatchDraw(); EndBatchDraw(); ``` 绘图函数全放入之前的while死循环中去 避免移动太快可以使用Sleep(300); 让它延迟300毫秒 移动一个很简单 怎么让所有的一起动? 同初始化一样使用for循环一起移动吗? 但是还有一个dir方向问题 如果使用了for循环 让它向下 那么三个都会一起平移向下 没有调头的感觉 想要的效果应该是 调头之后 后面的依次补上前一个的位置 所以 跟着这个思路 后面的coor等于前面的coor 先使用枚举 搞出方向方便一会switch用 ```cpp enum DIR //蛇的方向 { UP, DOWN, LEFT, RIGHT, }; ``` 再把简单的识别键盘操作的函数写了 在向右时肯定不能向左 所以加个判断 ```cpp void KeyControl() { if (GetAsyncKeyState(VK_UP) && snake.dir != DOWN) { snake.dir = UP; } if (GetAsyncKeyState(VK_DOWN) && snake.dir != UP) { snake.dir = DOWN; } if (GetAsyncKeyState(VK_LEFT) && snake.dir != RIGHT) { snake.dir = LEFT; } if (GetAsyncKeyState(VK_RIGHT) && snake.dir != LEFT) { snake.dir = RIGHT; } } ``` 且不管蛇身如何移动 蛇头移动 ```cpp switch (snake.dir) { case UP: snake.coor[0].y-=snake.speed; break; case DOWN: snake.coor[0].y+=snake.speed; break; case LEFT: snake.coor[0].x-=snake.speed; break; case RIGHT: snake.coor[0].x+=snake.speed; break; } ``` 蛇身跟随头移动 从尾巴开始 一个接一个替代前一个的位置 头部是在不断移动的 如果从头部开始 后一个替代前一个就可能重叠 ```cpp for (int i = snake.size - 1; i > 0; i--) snake.coor[i] = snake.coor[i - 1]; ``` 顺序换一下 因为一开始初始化了 蛇头一直在向右移动 尾巴应该先跟上 ```cpp void SnakeMove(){ //蛇身跟随头移动 for (int i = snake.size-1; i >0; i--){ snake.coor[i] = snake.coor[i - 1]; } //蛇头移动 switch (snake.dir){ case UP: snake.coor[0].y-=snake.speed; break; case DOWN: snake.coor[0].y+=snake.speed; break; case LEFT: snake.coor[0].x-=snake.speed; break; case RIGHT: snake.coor[0].x+=snake.speed; break; } } ``` 移动上没问题了 但是出现了一个跑出屏幕的问题 两种解决方案 1、让它从屏幕另一边出来 2、撞墙游戏结束 怎么让它从屏幕另一边出来 在蛇移动的时候加一个判断 如果蛇头的y<0了 那么意味着跑上面去了 让它从下面冒出来的话 x轴的数值是不用变的 把y=0变成y=窗口高度就行了 ![[easyx-image205.png]] 为了可变性 所以跟蛇身长度一样 使用宏定义 去把窗口的宽、高写在前面 ```cpp #define Win_width 640 #define Win_height 480 initgraph(Win_width, Win_height); ``` 再严谨点 其实圆心加半径(y+半径小于0时)就已经出去了 ```cpp #define SNAKE_BODY 5 ``` 初始化蛇的函数 把原来的5 改成SNAKE_BODY ```cpp for (int i = 0; i < snake.size; i++) { solidcircle(snake.coor[i].x, snake.coor[i].y, SNAKE_BODY); } ``` 蛇移动的函数里 每个方向上都加上判断 ```cpp if (snake.coor[0].y+ SNAKE_BODY <= 0) { snake.coor[0].y = Win_height; } ``` ![[easyx-image206.png]] OK 以此类推 ```cpp //移动蛇 void SnakeMove() { //蛇身跟随头移动 for (int i = snake.size-1; i >0; i--) { snake.coor[i] = snake.coor[i - 1]; } //蛇头移动 switch (snake.dir) { case UP: snake.coor[0].y-=snake.speed; if (snake.coor[0].y+ SNAKE_BODY <= 0) { snake.coor[0].y = Win_height; } break; case DOWN: snake.coor[0].y+=snake.speed; if (snake.coor[0].y + SNAKE_BODY >= Win_height) { snake.coor[0].y = 0; } break; case LEFT: snake.coor[0].x-=snake.speed; if (snake.coor[0].x + SNAKE_BODY <= 0) { snake.coor[0].x = Win_width; } break; case RIGHT: snake.coor[0].x+=snake.speed; if (snake.coor[0].x + SNAKE_BODY >= Win_width) { snake.coor[0].x = 0; } break; } ``` 实现出界跳转 蛇的初始化 移动 转弯 越界问题都解决了 接下来就是生长的问题了 生长肯定是要吃到食物 所以得再定义一个食物类型 得有坐标 大小 颜色 以及是否被吃 ```cpp //定义食物结构 struct Food { int x; int y; int r; bool flag;//布尔类型 只有0/1 可以用来判断是否被吃 DWORD color; }food; ``` 在初始化里面 因为食物应该是随机出现在屏幕上的 所以使用rand() 让rand()%窗口宽 那么它一定为0~窗口宽的范围内 同理 ```cpp food.x = rand() % Win_width; food.y = rand() % Win_height; ``` 但这样不够 如果不设置随机数种子 那么每次产生的都是固定的整数 而设置随机数种子需要头文件#include ```cpp srand(); ``` 一般将时间当做种子 因为时间是在不断变化的 ```cpp srand(GetTickCount()); GetTickCount();是获取系统开机 到现在所经过的毫秒数 把它返回给srand 那就是随机种子了 ``` 颜色这里也可以随机产生 这就很巧妙 RGB是获取三原色的调色嘛 红绿蓝各自在0~255 那么如果想要全部的颜色就把随机数%上256 余数就落在0~255里面了 ```cpp food.color = RGB(rand() % 256, rand() % 256, rand() % 256); ``` 大小也能随机 那就是2~5 ```cpp food.r = rand() % 3 + 2; ``` 初始化的时候肯定没被吃嘛 ```cpp food.flag = true; ``` 那么接下来就是绘制食物 ```cpp //绘制食物 if (food.flag) { solidcircle(food.x, food.y, food.r); } ``` 如何判断吃到与否? ![[easyx-image207.png]] 四种情况 分别从上下左右四个方向吃 可以用相切判断 但会显得判断语句非常长 且把蛇头的圆心 触碰到食物的边界(4个点)当做吃到食物 ```cpp void Eatfood() { if (snake.coor[0].x >= food.x - food.r && snake.coor[0].x <= food.x + food.r && snake.coor[0].y >= food.y - food.r && snake.coor[0].y <= food.y + food.r && food.flag == 1) { …… } } ``` 如果吃到了 那么 蛇身应该变长1 食物应该消失 ```cpp snake.size++; food.flag = 0; ``` 食物消失了应该再生成一个 ```cpp if (food.flag == 0) { food.x = rand() % Win_width; food.y = rand() % Win_height; food.flag = 1; food.color = RGB(rand() % 256, rand() % 256, rand() % 256); food.r = rand() % 4 + 3; } ``` 所以: ```cpp #include #include #include #define Win_width 640 #define Win_height 480 #define SNAKE_BODY 5 #define SNAKE_NUM 500 enum DIR //蛇的方向 { UP, DOWN, LEFT, RIGHT, }; //定义蛇结构 struct Snake { int size; int dir; int speed; POINT coor[SNAKE_NUM]; }snake; //定义食物结构 struct Food { int x; int y; int r; bool flag;//布尔类型 只有0/1 可以用来判断是否被吃 DWORD color; }food; //数据初始化 void GameInit() { //初始化图像窗口 initgraph(Win_width, Win_height); //设置随机数种子 获取开机时间到现在的毫秒数 返回当种子 srand(GetTickCount()); //初始化蛇 三节 向右 速度为10 snake.size = 3; snake.speed = 10; snake.dir=RIGHT; for (int i = snake.size - 1; i >= 0; i--) { snake.coor[i].x = 40-10 * i; snake.coor[i].y = 10; } //初始化食物 food.x = rand() % Win_width; food.y = rand() % Win_height; food.color = RGB(rand() % 256, rand() % 256, rand() % 256); food.r = rand() % 4 + 3; food.flag = 1; } void GameDraw() { //设置背景颜色 setbkcolor(RGB(206, 212, 240)); cleardevice(); //绘制蛇 for (int i = 0; i < snake.size; i++) { solidcircle(snake.coor[i].x, snake.coor[i].y, SNAKE_BODY); } //绘制食物 if (food.flag==1) { setfillcolor(food.color); solidcircle(food.x, food.y, food.r); } } //移动蛇 void SnakeMove(){ //蛇身跟随头移动 for (int i = snake.size-1; i >0; i--){ snake.coor[i] = snake.coor[i - 1]; } //蛇头移动 switch (snake.dir) { case UP: snake.coor[0].y-=snake.speed; if (snake.coor[0].y+ SNAKE_BODY <= 0) { snake.coor[0].y = Win_height; } break; case DOWN: snake.coor[0].y+=snake.speed; if (snake.coor[0].y + SNAKE_BODY >= Win_height) { snake.coor[0].y = 0; } break; case LEFT: snake.coor[0].x-=snake.speed; if (snake.coor[0].x + SNAKE_BODY <= 0) { snake.coor[0].x = Win_width; } break; case RIGHT: snake.coor[0].x+=snake.speed; if (snake.coor[0].x + SNAKE_BODY >= Win_width) { snake.coor[0].x = 0; } break; } } void KeyControl() { if (GetAsyncKeyState(VK_UP) && snake.dir != DOWN) { snake.dir = UP; } if (GetAsyncKeyState(VK_DOWN) && snake.dir != UP) { snake.dir = DOWN; } if (GetAsyncKeyState(VK_LEFT) && snake.dir != RIGHT) { snake.dir = LEFT; } if (GetAsyncKeyState(VK_RIGHT) && snake.dir != LEFT) { snake.dir = RIGHT; } } void Eatfood() { if (snake.coor[0].x >= food.x - food.r && snake.coor[0].x <= food.x + food.r && snake.coor[0].y >= food.y - food.r && snake.coor[0].y <= food.y + food.r && food.flag == 1) { snake.size++; food.flag = 0; } if (food.flag == 0) { food.x = rand() % Win_width; food.y = rand() % Win_height; food.flag = 1; food.color = RGB(rand() % 256, rand() % 256, rand() % 256); food.r = rand() % 4 + 3; } } int main() { GameInit(); BeginBatchDraw(); while (true){ GameDraw(); SnakeMove(); FlushBatchDraw(); KeyControl(); Eatfood(); Sleep(240); } EndBatchDraw(); getchar(); closegraph(); return 0; } ``` 这样一来 基本功能就都具备了 最终版 ```cpp #include #include #include #include #include ``` #pragma comment (lib,"Winmm.lib") ```cpp #define Win_width 640 #define Win_height 480 #define SNAKE_BODY 5 #define SNAKE_NUM 500 void PlayMusic(const char* FileName) { char cmdString[50] = "open "; strcat_s(cmdString, FileName); strcat_s(cmdString, " alias bkmusic"); mciSendString("close bkmusic", NULL, 0, NULL);//把上一次的音乐关掉 mciSendString(cmdString, NULL, 0, NULL);//打开音乐 mciSendString("play bkmusic repeat", NULL, 0, NULL);//播放音乐 } void change() { HWND hnd = GetHWnd(); SetWindowText(hnd, "纯享版贪吃蛇W"); } enum DIR //蛇的方向 { UP, DOWN, LEFT, RIGHT, }; //定义蛇结构 struct Snake { int size; int dir; int speed; POINT coor[SNAKE_NUM]; int score; }snake; //定义食物结构 struct Food { int x; int y; int r; bool flag;//布尔类型 只有0/1 可以用来判断是否被吃 DWORD color; }food; //数据初始化 void GameInit() { //初始化图像窗口 initgraph(Win_width, Win_height); //设置随机数种子 获取开机时间到现在的毫秒数 返回当种子 srand(GetTickCount()); //初始化蛇 三节 向右 速度为10 snake.size = 3; snake.speed = 10; snake.dir=RIGHT; snake.score = 0; for (int i = snake.size - 1; i >= 0; i--) { snake.coor[i].x = 40-10 * i; snake.coor[i].y = 10; } //初始化食物 food.x = rand() % (Win_width-10); food.y = rand() % (Win_height-10); food.color = RGB(rand() % 256, rand() % 256, rand() % 256); food.r = 5; food.flag = 1; } void GameDraw() { //设置背景颜色 setbkcolor(RGB(206, 212, 240)); cleardevice(); //绘制蛇 for (int i = 0; i < snake.size; i++) { solidcircle(snake.coor[i].x, snake.coor[i].y, SNAKE_BODY); } //绘制食物 if (food.flag==1) { setfillcolor(food.color); solidcircle(food.x, food.y, food.r); } //显示分数 char temp[20] = ""; sprintf(temp, "分数:%d", snake.score); setbkmode(TRANSPARENT); outtextxy(20, 20, temp); } //移动蛇 void SnakeMove() { //蛇身跟随头移动 for (int i = snake.size-1; i >0; i--) { snake.coor[i] = snake.coor[i - 1]; } //蛇头移动 switch (snake.dir) { case UP: snake.coor[0].y-=snake.speed; if (snake.coor[0].y+ SNAKE_BODY <= 0) { snake.coor[0].y = Win_height; } break; case DOWN: snake.coor[0].y+=snake.speed; if (snake.coor[0].y + SNAKE_BODY >= Win_height) { snake.coor[0].y = 0; } break; case LEFT: snake.coor[0].x-=snake.speed; if (snake.coor[0].x + SNAKE_BODY <= 0) { snake.coor[0].x = Win_width; } break; case RIGHT: snake.coor[0].x+=snake.speed; if (snake.coor[0].x + SNAKE_BODY >= Win_width) { snake.coor[0].x = 0; } break; } } void KeyControl() { if (GetAsyncKeyState(VK_UP) && snake.dir != DOWN) { snake.dir = UP; } if (GetAsyncKeyState(VK_DOWN) && snake.dir != UP) { snake.dir = DOWN; } if (GetAsyncKeyState(VK_LEFT) && snake.dir != RIGHT) { snake.dir = LEFT; } if (GetAsyncKeyState(VK_RIGHT) && snake.dir != LEFT) { snake.dir = RIGHT; } } void Eatfood() { if (snake.coor[0].x >= food.x - food.r && snake.coor[0].x <= food.x + food.r && snake.coor[0].y >= food.y - food.r && snake.coor[0].y <= food.y + food.r && food.flag == 1) { snake.size++; snake.score += 10; food.flag = 0; } if (food.flag == 0) { food.x = rand() % Win_width; food.y = rand() % Win_height; food.flag = 1; food.color = RGB(rand() % 256, rand() % 256, rand() % 256); food.r = 5; } } int main() { GameInit(); PlayMusic("D:\\easyX素材\\BGM4.mp3"); change(); BeginBatchDraw(); while (true) { GameDraw(); SnakeMove(); FlushBatchDraw(); Eatfood(); KeyControl(); Sleep(220); } EndBatchDraw(); getchar(); closegraph(); return 0; } ``` ## 井字棋 ```cpp #include #include #define BKWIDTH 300 #define BKHEIGHT 300 #define PICWIDTH BKWIDTH/3 #define PICHETGHT BKHEIGHT/3 int arr[3][3]; void InitDraw() { for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { arr[i][j] = -1; } } initgraph(BKWIDTH, BKHEIGHT); setbkcolor(RGB(195, 195, 195)); cleardevice(); setlinecolor(RGB(237, 28, 36)); setlinestyle(PS_SOLID | PS_JOIN_BEVEL,1); line(0, 0, BKWIDTH, 0); line(0, PICHETGHT, BKWIDTH, PICHETGHT); line(0, 2*PICHETGHT, BKWIDTH, 2 * PICHETGHT); line(0, BKHEIGHT, BKWIDTH, BKHEIGHT); line(0, 0, 0, BKHEIGHT); line(PICWIDTH, 0, PICWIDTH, BKHEIGHT); line(2*PICWIDTH, 0, 2 * PICWIDTH, BKHEIGHT); line(BKWIDTH, 0, BKWIDTH, BKHEIGHT); } void DrawHook(int x, int y) { IMAGE hook; loadimage(&hook, "D:\\easyX素材\\勾.png",PICWIDTH-1,PICHETGHT-1); putimage(x+1, y+1, &hook); } void DrawFork(int x, int y) { IMAGE fork; loadimage(&fork, "D:\\easyX素材\\叉.png", PICWIDTH - 1, PICHETGHT - 1); putimage(x + 1, y + 1, &fork); } int Region1(short x,short y) { if (x >= 0 && x <= BKWIDTH / 3 && y>=0 && y<=BKHEIGHT/3) return 1; else return 0; } int Region2(short x, short y) { if (x >= PICWIDTH && x <= 2*PICWIDTH && y >= 0 && y <= PICHETGHT) return 1; else return 0; } int Region3(short x, short y) { if (x >= 2*PICWIDTH && x <= BKWIDTH && y >= 0 && y <= PICHETGHT) return 1; else return 0; } int Region4(short x, short y) { if (x >= 0 && x <= PICWIDTH && y >= PICHETGHT && y <= 2*PICHETGHT) return 1; else return 0; } int Region5(short x, short y) { if (x >= PICWIDTH && x <= 2*PICWIDTH && y >= PICHETGHT && y <= 2 * PICHETGHT) return 1; else return 0; } int Region6(short x, short y) { if (x >= 2*PICWIDTH && x <= BKWIDTH && y >= PICHETGHT && y <= 2 * PICHETGHT) return 1; else return 0; } int Region7(short x, short y) { if (x >= 0 && x <= PICWIDTH && y >= 2*PICHETGHT && y <= BKHEIGHT) return 1; else return 0; } int Region8(short x, short y) { if (x >= PICWIDTH && x <= 2*PICWIDTH && y >= 2*PICHETGHT && y <= BKHEIGHT) return 1; else return 0; } int Region9(short x, short y) { if (x >= 2*PICWIDTH && x <= BKWIDTH && y >= 2*PICHETGHT && y <= BKHEIGHT) return 1; else return 0; } void Play() { ExMessage msg; if (peekmessage(&msg, EX_MOUSE)) { switch (msg.message) { case WM_LBUTTONDOWN: if (Region1(msg.x, msg.y)) { DrawHook(0, 0); arr[0][0] = { 1 }; } if (Region2(msg.x, msg.y)) { DrawHook(PICWIDTH, 0); arr[0][1] = { 1 }; } if (Region3(msg.x, msg.y)) { DrawHook(2 * PICWIDTH, 0); arr[0][2] = { 1 }; } if (Region4(msg.x, msg.y)) { DrawHook(0, PICHETGHT); arr[1][0] = { 1 }; } if (Region5(msg.x, msg.y)) { DrawHook(PICWIDTH, PICHETGHT); arr[1][1] = { 1 }; } if (Region6(msg.x, msg.y)) { DrawHook(2 * PICWIDTH, PICHETGHT); arr[1][2] = { 1 }; } if (Region7(msg.x, msg.y)) { DrawHook(0, 2 * PICHETGHT); arr[2][0] = { 1 }; } if (Region8(msg.x, msg.y)) { DrawHook(PICWIDTH, 2 * PICHETGHT); arr[2][1] = { 1 }; } if (Region9(msg.x, msg.y)) { DrawHook(2 * PICWIDTH, 2 * PICHETGHT); arr[2][2] = { 1 }; } break; case WM_RBUTTONDOWN: if (Region1(msg.x, msg.y)) { DrawFork(0, 0); arr[0][0] = { 0 }; } if (Region2(msg.x, msg.y)) { DrawFork(PICWIDTH, 0); arr[0][1] = { 0 }; } if (Region3(msg.x, msg.y)) { DrawFork(2 * PICWIDTH, 0); arr[0][2] = { 0 }; } if (Region4(msg.x, msg.y)) { DrawFork(0, PICHETGHT); arr[1][0] = { 0 }; } if (Region5(msg.x, msg.y)) { DrawFork(PICWIDTH, PICHETGHT); arr[1][1] = { 0 }; } if (Region6(msg.x, msg.y)) { DrawFork(2 * PICWIDTH, PICHETGHT); arr[1][2] = { 0 }; } if (Region7(msg.x, msg.y)) { DrawFork(0, 2 * PICHETGHT); arr[2][0] = { 0 }; } if (Region8(msg.x, msg.y)) { DrawFork(PICWIDTH, 2 * PICHETGHT); arr[2][1] = { 0 }; } if (Region9(msg.x, msg.y)) { DrawFork(2 * PICWIDTH, 2 * PICHETGHT); arr[2][2] = { 0 }; } break; } } } void Judgement() { int numy, numn; int result = -1; int i, j; for (i = 0; i < 3 && result==-1 ; i++) { numy = 0, numn = 0; for (j = 0; j < 3; j++) { if (arr[i][j] == 1) { numy++; } if(arr[i][j] == 0) { numn++; } } if (numy == 3) { result = 0; } if (numn == 3) { result = 1; } } if (result == -1) { for (j = 0; j < 3 && result == -1; j++) { numy = 0, numn = 0; for (i = 0; i < 3; i++) { if (arr[i][j] == 1) { numy++; } if (arr[i][j] == 0) { numn++; } } if (numy == 3) { result = 0; } if (numn == 3) { result = 1; } } } if (result == -1) { numy = numn = 0; for (i = 0; i < 3; i++) { if (arr[i][i] == 1) { numy++; } if (arr[i][i] == 0) { numn++; } } if (numy == 3) { result = 0; } if (numn == 3) { result = 1; } } if (result == -1) { numy = numn = 0; for (i = 0; i < 3; i++) { if(arr[i][3-i-1]==1) { numy++; } if (arr[i][3 - i - 1] == 0) { numn++; } } if (numy == 3) { result = 0; } if (numn == 3) { result = 1; } } if (result == -1) { if (arr[0][0] != -1 && arr[0][1] != -1 && arr[0][2] != -1 && arr[1][0] != -1 && arr[1][1] != -1 && arr[1][2] != -1 && arr[2][0] != -1 && arr[2][1] != -1 && arr[2][2] != -1) result = 2; } if (result == 0) { MessageBox(NULL, "勾赢,游戏结束", "游戏结束", MB_OKCANCEL); getchar(); } if (result == 1) { MessageBox(NULL, "叉赢,游戏结束", "游戏结束", MB_OKCANCEL); getchar(); } if (result == 2) { MessageBox(NULL, "平局,游戏结束", "游戏结束", MB_OKCANCEL); getchar(); } } int main() { InitDraw(); while (true) { Play(); Judgement(); } getchar(); closegraph(); return 0; } ``` ### release ![[easyx-image212.png]] 发布版 可直接发送给未装编译器的电脑 ## 判断一个点是否在圆内 ![[easyx-image213.png]] 判断欧几里得距离即可 ```cpp #include pow(a,b); a的b次方 sqrt(double c); c的平方根 ``` ## 两圆相交范围判断 ![[easyx-image214.jpeg]] ## 附件 > 📄 C进阶 图形化 easyX.docx(原 word 笔记):[[C进阶 图形化 easyX.docx|C进阶 图形化 easyX.docx]] --- ⬅️ [[13-C语言与MySQL连接|C语言与MySQL连接]] 🏠 [[00-编程语言|00-编程语言]] ➡️ [[15-汇编|汇编]]